package org.zfes.snowier.common.monitor.mysql;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.zfes.snowier.common.monitor.http.okhttp3.OkHttpManager;
import org.zfes.snowier.common.monitor.utils.ConfigReader;

import com.alibaba.fastjson.JSON;
public class MysqlMonitor {
	private static final String showStatusSql="show global status where Variable_name in('Questions','Com_select','Com_insert','Com_delete','Com_update','Com_begin','Com_commit','Com_rollback','Max_used_connections','Threads_connected','Connections','Threads_running','Bytes_received','Bytes_sent','Open_tables','Uptime');"; 
	private static final long collectPeriod=5;
	private static final long collectTimeout=1000*8;
	
	
	private  String instanceKey;//
	private  String serverDomains;//
	private  String serverRoute;
	private  String jsonDatakey;
	private String host;
	private Integer port;
	private String dbname;
	private String username;
	private String password;
	private String jdbcDriver;
	private String jdbcUrl;
	
	private String configFile;
	
	
	private ScheduledExecutorService scheduledThreadPool ;
	
	private  MonitorData lastMonitor;
	
	private Connection conn;//维护一个长连接

	private MysqlMonitor(String configFile) {
		initConfig(configFile);
		scheduledThreadPool = Executors.newScheduledThreadPool(1);
		Runtime.getRuntime().addShutdownHook(new Thread(()->DBUtil.closeResource(null, null, conn)));//监听jvm关闭
	
	}

	public static MysqlMonitor newInstance(String configFile) {
        return new MysqlMonitor(configFile);
	}
	
	
	public  void startMonitor() {
		scheduledThreadPool.scheduleAtFixedRate(()->{
			
			Optional<MonitorData> dataOp = excuseStatusQuery();
			if(dataOp.isPresent()) {
				MonitorData nowMonitorData=dataOp.get().setDate(new Date()).setHost(host).setPort(port).setInstanceKey(instanceKey);
				if(lastMonitor!=null) {
					if(nowMonitorData.getDate().getTime()-lastMonitor.getDate().getTime()<collectTimeout) {//获取数据超过8秒，则作废，不纳入统计
						Optional<MonitorData> datToBePush=calculate(lastMonitor,nowMonitorData); 
						try {
							doExcuteHttpPush(serverRoute, instanceKey , jsonDatakey ,JSON.toJSONString(datToBePush));
						}catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
				lastMonitor=nowMonitorData;
				
			}
			
		}, 0L,collectPeriod, TimeUnit.SECONDS);
		
		System.out.println("mysql-monitor-stared.....");
	}
	
	

	
	
    private void doExcuteHttpPush( String serverRoute,String instanceKey ,String jsonDatakey ,String jsonDataValue) {
    	Stream.of(serverDomains.split(",")).forEach(domain->{
    		System.out.println("--jsonDataValue--："+jsonDataValue);
    		 OkHttpManager.newIns()
    		 .addParam(jsonDatakey,jsonDataValue)
    		 .addParam("instanceKey", instanceKey)
 	 				.postAsync(domain+serverRoute, jsonResponse -> {
 	 					System.out.println("--jsonResponse--："+jsonResponse);
 	 				
 	 		});
    	});
    }
    
    
    private  Optional<MonitorData> excuseStatusQuery() {
		MonitorData data=new MonitorData();
		try {
			if(conn==null) {//空则去开启一个连接
				 conn = openConn();
			}else {//
				if(conn.isClosed()) {//如果已经关闭，则去开启一个连接
					conn = openConn();
				}
				if(!conn.isValid(2)) {//测试一下是否可用
					closeResource(null, null, conn);
					conn = openConn();
				}
			}
			
			Statement stmt=conn.createStatement();
			 ResultSet rs =stmt.executeQuery(showStatusSql);
			 while (rs != null && rs.next()){
				 String fieldName=rs.getString("Variable_name");
				 Integer value=rs.getInt("Value");
				 Field field=MonitorData.class.getDeclaredField(fieldName);
				 Method method =MonitorData.class.getMethod("set"+firstUpperCase(field.getName()),field.getType());
				 method.invoke(data, value);
	         }
			 DBUtil.closeResource(rs, stmt, null);
		}catch (Exception e) {
			e.printStackTrace();
			return Optional.empty();
		}
		return Optional.of(data);
	}

	private void initConfig(String configFile) {
		this.configFile=configFile;
		this.jdbcDriver=getConfig("mysqlJdbcDriver");
		this.host=getConfig("mysqlHost");
		this.port=Integer.valueOf(getConfig("mysqlPort"));
		this.dbname=getConfig("mysqlDbname");
		this.username=getConfig("mysqlUsername");
		this.password=getConfig("mysqlqPassword");
		this.jdbcUrl="jdbc:mysql://"+this.host+":"+this.port+"/"+this.dbname+"?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8";
		this.instanceKey=getConfig("mysqlInstanceKey");
		this.serverDomains=getConfig("myslServerDomains");
		this.serverRoute=getConfig("mysqlServerRoute");
		this.jsonDatakey=getConfig("mysqlJsonDatakey");
	}
	
	private  Connection openConn () {
		Connection connection=null;
		try {
			Class.forName(jdbcDriver);
			connection = DriverManager.getConnection(jdbcUrl,username,password);
		 } catch (SQLException |ClassNotFoundException e) {
	          e.printStackTrace();
	        throw new RuntimeException("获取连接失败： "+e);
		 }
		 return connection;
	}
	
	private  void closeResource(ResultSet rs, Statement st, Connection con) {
		try {
			if (rs != null) {
				rs.close();
			}
			if (st != null) {
				st.close();
			}
			if (con != null) {
				con.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	private  Optional<MonitorData> calculate(MonitorData last, MonitorData now) {
		if(now==null||last==null) {
			return Optional.empty();
		}
		MonitorData finalData=now.clone();
		Long seconds=(now.getDate().getTime()-last.getDate().getTime())/1000;
		finalData.setCom_insert((now.getCom_insert()-last.getCom_insert())/seconds.intValue());
		finalData.setCom_begin((now.getCom_begin()-last.getCom_begin())/seconds.intValue());
		finalData.setCom_select((now.getCom_select()-last.getCom_select())/seconds.intValue());
		finalData.setCom_delete((now.getCom_delete()-last.getCom_delete())/seconds.intValue());
		finalData.setCom_update((now.getCom_update()-last.getCom_update())/seconds.intValue());
		finalData.setCom_commit((now.getCom_commit()-last.getCom_commit())/seconds.intValue());
		finalData.setCom_rollback((now.getCom_rollback()-last.getCom_rollback())/seconds.intValue());
		finalData.setQuestions((now.getQuestions()-last.getQuestions())/seconds.intValue());
		finalData.setQps(finalData.getQuestions());
		int tps=((now.getCom_commit()+now.getCom_rollback())-(last.getCom_commit()+last.getCom_rollback()))/seconds.intValue();
		
		finalData.setTps(tps);
		return Optional.ofNullable(finalData);
	}

	
	

//	private  Map<String, String> getResultMap(ResultSet rs)   throws SQLException {  
//        Map<String, String> hm = new HashMap<String, String>();  
//        ResultSetMetaData rsmd = rs.getMetaData();  
//        int count = rsmd.getColumnCount();  
//        for (int i = 1; i <= count; i++) {  
//            String key = rsmd.getColumnLabel(i);  
//            String value = rs.getString(i);  
//            hm.put(key, value);  
//        }  
//        return hm;  
//	}  
	
	private  String firstUpperCase(String old){
        return old.substring(0, 1).toUpperCase()+old.substring(1);
    }
	
	//=======================================================================================
	
	
	private  String getConfig(String key) {
		Optional<String> valueOp =ConfigReader.getConfig(configFile, key);
		if(!valueOp.isPresent()) {
			throw new RuntimeException("未正确配置"+key);
		}	
		return valueOp.get();
	}
	
	
	

	
}
